import axios, {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  Method,
} from "axios";
import { CreateAxiosOptions } from "./AxiosTransForm";
import { AxiosCanceler } from "./AxiosCancel";
import { isFunction, cloneDeep } from "lodash-es";
import { RequestOptions, Result, UploadFileParams } from "types/axios";
import { ContentTypeEnum, RequestEnum } from "../enum/HttpEnum";
import qs from "qs";
// import { showLoading, hideLoading } from "@/utils/loading";

export class YAxios {
  private axiosInstance: AxiosInstance;
  private readonly options: CreateAxiosOptions;

  constructor(options: CreateAxiosOptions) {
    console.log("CreateAxiosOptions", options);
    this.options = options;
    this.axiosInstance = axios.create(options);
    this.setUpInterceptors();
  }

  private createAxios(config: CreateAxiosOptions): void {
    this.axiosInstance = axios.create(config);
  }

  private getTransForm() {
    const { transForm } = this.options;
    return transForm;
  }

  getAxios(): AxiosInstance {
    return this.axiosInstance;
  }

  configAxios(config: CreateAxiosOptions) {
    if (!this.axiosInstance) {
      return;
    }
    this.createAxios(config);
  }

  setHeader(headers: any): void {
    if (!this.axiosInstance) {
      return;
    }
    Object.assign(this.axiosInstance.defaults.headers, headers);
  }

  /**
   * 拦截器配置
   * @private
   */
  private setUpInterceptors() {
    const transForm = this.getTransForm();
    if (!transForm) {
      return;
    }
    const {
      requestInterceptors,
      requestInterceptorsCatch,
      responseInterceptors,
      responseInterceptorsCatch,
    } = transForm;

    const axiosCanceler = new AxiosCanceler();

    this.axiosInstance.interceptors.request.use(
      (config: CreateAxiosOptions) => {
        // 是否忽略重复请求
        const ignoreCancelToken = config.requestOptions?.ignoreCancelToken;
        ignoreCancelToken && axiosCanceler.addPending(config);

        // 配置config
        if (requestInterceptors && isFunction(requestInterceptors)) {
          config = requestInterceptors(config, this.options);
        }
        return config;
      },
      () => {}
    );
    requestInterceptorsCatch &&
      isFunction(responseInterceptorsCatch) &&
      this.axiosInstance.interceptors.request.use(
        undefined,
        requestInterceptorsCatch
      );

    this.axiosInstance.interceptors.response.use(
      (res: AxiosResponse<any>) => {
        res && axiosCanceler.removePending(res.config);
        // 噢诶之response
        if (responseInterceptors && isFunction(responseInterceptors)) {
          res = responseInterceptors(res);
        }
        return res;
      },
      () => {}
    );

    responseInterceptorsCatch &&
      isFunction(responseInterceptorsCatch) &&
      this.axiosInstance.interceptors.response.use(
        undefined,
        responseInterceptorsCatch
      );
  }

  supportFormData(config: AxiosRequestConfig) {
    const headers = config.headers || this.options.headers;
    const contentType = headers?.["Content-type"] || headers?.["content-type"];

    if (
      contentType !== ContentTypeEnum.FORM_URLENCODED ||
      !Reflect.has(config, "data") ||
      config.method?.toUpperCase() === RequestEnum.GET
    ) {
      return config;
    }
    return {
      ...config,
      data: qs.stringify(config.data, { arrayFormat: "brackets" }),
    };
  }

  uploadFile<T = any>(
    config: AxiosRequestConfig,
    params: UploadFileParams,
    options?: RequestOptions
  ): Promise<T> {
    const formData = new window.FormData();
    Object.keys(params).forEach((key) => {
      formData.append(key, params[key]);
    });
    return this.request(
      {
        ...config,
        method: "POST",
        data: formData,
        headers: {
          "Content-type": ContentTypeEnum.FORM_DATA,
        },
      },
      options
    );
    //formData.append(params.name || 'file', params.file, params.filename);
    // return this.axiosInstance.request<T>({
    //     ...config,
    //     method: 'POST',
    //     data: formData,
    //     headers: {
    //         'Content-type': ContentTypeEnum.FORM_DATA
    //     }
    // })
  }

  get<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "GET" }, options);
  }

  post<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "POST" }, options);
  }

  put<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "PUT" }, options);
  }

  delete<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "DELETE" }, options);
  }

  // 下载文件
  downLoad<T = any>(
    config: AxiosRequestConfig,
    downLoadFile: string,
    option?: RequestOptions,
    method: Method = "POST"
  ): Promise<T> {
    return this.request(
      { ...config, method: method },
      {
        ...option,
        downLoadFile: downLoadFile,
      }
    );
  }

  request<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    let conf: CreateAxiosOptions = cloneDeep(config);
    // conf.data.get("file")
    const transform = this.getTransForm();
    const { requestOptions } = this.options;
    const opt: RequestOptions = Object.assign(
      {},
      requestOptions,
      options
    ) as RequestOptions;
    const { beforeRequestHook, requestCatchHook, transFormRequestHook } =
      transform || {};
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt);
    }
    conf.requestOptions = opt;
    conf = this.supportFormData(conf);

    if (conf.requestOptions?.loading) {
      // showLoading();
    }

    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request<any, AxiosResponse<Result>>(conf)
        .then((res: AxiosResponse<Result>) => {
          console.log("res", res);
          if (transFormRequestHook && isFunction(transFormRequestHook)) {
            try {
              const ret = transFormRequestHook(res, opt);
              resolve(ret);
            } catch (err) {
              reject(err || new Error("request error!"));
            }
            return;
          } else {
            reject(res);
          }
        })
        .catch((e: Error | AxiosError) => {
          if (requestCatchHook && isFunction(requestCatchHook)) {
            reject(requestCatchHook(e, opt));
            return;
          }
          if (axios.isAxiosError(e)) {
            console.log("axios报错详情", e);
          }
          reject(e);
        })
        .finally(() => {
          if (conf.requestOptions?.loading) {
            // hideLoading();
          }
        });
    });
  }
}
